package com.googlecode.tidej.client.codemirror;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Timer;
import com.googlecode.tidej.client.codemirror.CodeMirror.Pos;
import com.googlecode.tidej.client.codemirror.CodeMirror.Token;
import com.googlecode.tidej.shared.model.Type;

public final class CodeMirror extends JavaScriptObject {

  public static native CodeMirror fromTextArea(Element textArea, Configuration config) /*-{
    return $wnd.CodeMirror.fromTextArea(textArea, config);
  }-*/;

  public static native CodeMirror create(Element element, Configuration config) /*-{
    return $wnd.CodeMirror(element, config);
  }-*/;
  
  public void refresh() {
    new Timer() {
      @Override
      public void run() {
        refreshImpl();
      }
    }.schedule(1);
  }
  
  public native void focus() /*-{
    this.focus();
  }-*/;

  
  public native Pos getCursor(boolean selectionStart) /*-{
    return this.getCursor(selectionStart);
  }-*/;

  public native Pos getCursor() /*-{
    return this.getCursor();
  }-*/;

  
  public native Token getTokenAt(Pos pos) /*-{
    return this.getTokenAt(pos);
  }-*/;
  
  public native void refreshImpl() /*-{
    if (this['refresh']) {
      this.refresh();
    }
  }-*/;

  public native void replaceRange(String s, Pos pos) /*-{
    this.replaceRange(s, pos);
  }-*/;

  public void setValue(String s) {
    setValueImpl(s);
    refresh();
  }
  
  private native void setValueImpl(String string) /*-{
    this.setValue(string);
  }-*/;

  public native String getValue() /*-{
    return this.getValue();
  }-*/;
  
  protected CodeMirror() {
  }

  
  public static final class Pos extends JavaScriptObject {
    protected Pos() {
    }
    public static native Pos create(int l, int c) /*-{
      return {line: l, ch: c};
    }-*/;
    public native int getLine() /*-{
      return this.line;
    }-*/;
    public native int getCh() /*-{
      return this.ch;
    }-*/;
  }
  
  public static final class Token extends JavaScriptObject {
    protected Token() {
    }
    public native String getString() /*-{
      return this.string;
    }-*/;
    public native String getClassName() /*-{
      return this.className;
    }-*/;
    public native int getStart() /*-{
      return this.start;
    }-*/;
    public native int getEnd() /*-{
      return this.end;
    }-*/;
    public native State getState() /*-{
      return this.state;
    }-*/;
    
    public String toDebugString() {
      return "'" + getString() + "'[" + getStart() + "-" + getEnd() + "]:" + getClassName();
    }
  }
  
  public static final class State extends JavaScriptObject {
    protected State() {
    }
    public native LocalVar getLocalVars() /*-{
      return this.localVars;
    }-*/;
    
    public native JsArrayString getReAllowed() /*-{
      return this.reAllowed;
    }-*/;
    
    public native boolean getKwAllowed() /*-{
      return this.kwAllowed;
    }-*/;

  }
  
  public static final class LocalVar extends JavaScriptObject {
    public static final native LocalVar create(String name, Object object, Type type, LocalVar next) /*-{
      return {name: name, type:type, next:next};
    }-*/;
    
    protected LocalVar() {
    }
    public native LocalVar getNext() /*-{
      return this.next;
    }-*/;
    
    public native String getName() /*-{
      return this.name;
    }-*/;
    
    public native Type getType() /*-{
      return this.type;
    }-*/;
    
    public native Object getObject() /*-{
      return this.object;
    }-*/;
    
    public static String toString(LocalVar lv) {
      StringBuilder sb = new StringBuilder();
      while(lv != null) {
        sb.append(lv.getName());
        sb.append(": ");
        sb.append(lv.getType());
        sb.append(", ");
        lv = lv.getNext();
      } 
      return sb.toString();
    }
    
  }
  
  public static final class ParserConfig extends JavaScriptObject {
    public static final native ParserConfig create(String name) /*-{
      return {name:name};
    }-*/;
    
    protected ParserConfig() {
    }
    
    public final native void setLocalVariables(LocalVar lv) /*-{
      this.localVars = lv;
    }-*/;
  }
  
  public static final class Configuration extends JavaScriptObject {
    protected Configuration() {
    }
   
    public static native Configuration create() /*-{
      return {'document': $wnd.document};
    }-*/;
    
    public native void setMode(String mode) /*-{
      this['mode'] = mode;
    }-*/;
    
    public native void setLineNumbers(boolean b) /*-{
      this['lineNumbers'] = b;
    }-*/;
    
    public native void setMode(ParserConfig mode) /*-{
      this['mode'] = mode;
    }-*/;
  }

  public native Pos posFromIndex(int index) /*-{
    return this.posFromIndex(index);
  }-*/;

  public native void setSelection(Pos start, Pos end) /*-{
    this.setSelection(start, end);
  }-*/;

  public native void replaceSelection(String text) /*-{
    this.replaceSelection(text);
  }-*/;

}
